package DB.FreeBaseImport;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import view.ViewFunctions;

import DB.Connection.DBConnectionManager;

import commons.Constants;
import commons.Entities.LegenCharacter;

/**
 * a class which handles the import from freebase
 */
public class FreeBaseImport implements Runnable {

	private static final Log logger = LogFactory.getLog(FreeBaseImport.class);

	private static String freebaseUrl = Constants.FREEBASE_IMPORT_URL;
	private static String freebaseFileExtension = Constants.FREEBASE_IMPORT_FILE_EXTENSION;
	private static String freebaseFileDelimiter = Constants.FREEBASE_IMPORT_FILE_DELIMITER;

	private static int importBulkSize = Constants.FREEBASE_IMPORT_BULK_SIZE;
	private static int importTimePeriod = Constants.FREEBASE_IMPORT_TIME_PERIOD_IN_DAYS;

	private BufferedReader in;
	private Map<String, String> dateTimeMap;
	private boolean byUser;

	private DBConnectionManager connManager = DBConnectionManager.get();

	public FreeBaseImport(boolean byUser) {
		this.byUser = byUser;
		dateTimeMap = new HashMap<String, String>();
	}

	/**
	 * load All Tables From FreeBase
	 */
	public void run() {
		logger.info("Updating the the data from freebase...");
		int days = connManager.getDaysSinceLastFreebaseUpdateDate();
		if ((byUser) || (days == -1) || (days >= importTimePeriod)) {
			connManager.setLastFreebaseUpdateDate();
			loadDateTimeMap();
			loadCharacters();
		} else {
			logger.info("Update from freebase is not required currently.");
		}
		if (byUser) {
		System.out.println("this works?");
			ViewFunctions.get().updateGUIFinishedUpdatingDBFromFreebase();
		}
	}

	/**
	 * loads the date and time
	 */
	private void loadDateTimeMap() {

		String inputLine;
		dateTimeMap.clear();
		openFile("fictional_date_time", 0);

		try {
			while ((inputLine = in.readLine()) != null) {
				// parse the line
				String[] attrVals = inputLine.split(freebaseFileDelimiter, -1);
				dateTimeMap.put(attrVals[1], attrVals[2]);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		closeFile();
	}

	/**
	 * load all characters from freebase
	 */
	private void loadCharacters() {
		int i = 0;
		List<LegenCharacter> lcList = new ArrayList<LegenCharacter>();
		LegenCharacter lc = null;

		openFile("fictional_character", 0);

		do {
			lc = parseCharacterLine();
			// we ignore characters with no name.... (reasonable right?)
			if ((lc == null) || (lc.getCharacterName() == null))
				continue;
			lcList.add(lc);
			i++;
			if (lcList.size() == importBulkSize) {
				connManager.insertToCharacterTableInBatch(lcList);
				lcList.clear();
				closeFile();
				openFile("fictional_character", i);
				logger.debug(i);
			}
		} while (lc != null);

		if (!lcList.isEmpty()) {
			connManager.insertToCharacterTableInBatch(lcList);
		}
		closeFile();
	}

	/**
	 * open the tsv file from freebase and read the first line
	 * 
	 * @param fileName
	 */
	private void openFile(String fileName, int line) {

		URL url;
		try {
			url = new URL(freebaseUrl + fileName + freebaseFileExtension);
			in = new BufferedReader(new InputStreamReader(url.openStream()));

			// read the first line
			in.readLine();
			for (int i = 0; i < line; i++) {
				in.readLine();
			}

		} catch (Exception e) {
			logger.error("Error while trying to open freebase data dump file, in url : " + freebaseUrl + fileName + freebaseFileExtension, e);
		}
	}

	/**
	 * close the tsv file from freebase
	 */
	private void closeFile() {

		try {
			in.close();
		} catch (IOException e) {
			logger.error("Error while trying to close freebase data dump file.", e);
		}
	}

	/**
	 * read a line from "fictional_character" file and map it to a Character
	 * object
	 * 
	 * @precondition the method openFile("fictional_character") was called
	 *               before this one
	 * @return currCharacter
	 */
	private LegenCharacter parseCharacterLine() {

		String[] attrVals = { "" };
		String inputLine;
		LegenCharacter currCharacter = new LegenCharacter();

		try {
			inputLine = in.readLine();

			if (inputLine == null)
				return null;
			// parse the line
			attrVals = inputLine.split(freebaseFileDelimiter, -1);
			// map the parsed line into a Character object
			for (int i = 0; i < attrVals.length; i++) {
				setCharacterAttr(currCharacter, i, attrVals[i]);
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

		return currCharacter;
	}

	/**
	 * sets all characters attributes from attrVal
	 * 
	 * @param currCharacter
	 * @param i
	 * @param attrVal
	 */
	private void setCharacterAttr(LegenCharacter currCharacter, int i, String attrVal) {

		if (attrVal.equals(""))
			attrVal = null;

		switch (i) {
			case 0:
				currCharacter.setCharacterName(attrVal);
				break;
			case 1:
				currCharacter.setCharacterFBID(attrVal);
				break;
			case 2:
				currCharacter.setBasedOn(attrVal);
				break;
			case 3:
				currCharacter.setPlaceOfBirth(attrVal);
				break;
			case 4:
				currCharacter.setGender(attrVal);
				break;
			case 5:
				currCharacter.setSpecies(attrVal);
				break;
			case 6:
				currCharacter.setParents(attrVal);
				break;
			case 7:
				currCharacter.setOccupation(attrVal);
				break;
			case 8:
				currCharacter.setRank(attrVal);
				break;
			case 9:
				currCharacter.setOrganizations(attrVal);
				break;
			case 10:
				currCharacter.setPowersOrAbilities(attrVal);
				break;
			case 11:
				currCharacter.setAppearsInTheseFictionalUniverses(attrVal);
				break;
			case 12:
				currCharacter.setChildren(attrVal);
				break;
			case 13:
				break;
			case 14:
				break;
			case 15:
				currCharacter.setCharacterCreatedBy(attrVal);
				break;
			case 16:
				break;
			case 17:
				currCharacter.setQuotations(attrVal);
				break;
			case 18:
				break;
			case 19:
				currCharacter.setEthnicity(attrVal);
				break;
			case 20:
				currCharacter.setEducation(attrVal);
				break;
			case 21:
				currCharacter.setHasPossessed(attrVal);
				break;
			case 22:
				currCharacter.setMedicalConditions(attrVal);
				break;
			case 23:
				currCharacter.setDateOfBirth(dateTimeMap.get(attrVal));
				break;
			case 24:
				if (attrVal != null)
					currCharacter.setHeight(Float.parseFloat(attrVal));
				break;
			case 25:
				if (attrVal != null)
					currCharacter.setWeight(Float.parseFloat(attrVal));
				break;
			case 26:
				currCharacter.setPlacesLived(attrVal);
				break;
			default:
				break;
		}
	}
}
